package com.lry.basic.jvm.asm.transform_class.demo9;

import aj.org.objectweb.asm.ClassVisitor;
import aj.org.objectweb.asm.MethodVisitor;
import aj.org.objectweb.asm.Opcodes;
import aj.org.objectweb.asm.Type;

import static aj.org.objectweb.asm.Opcodes.*;


public class MethodParamVisitor extends ClassVisitor {

    public MethodParamVisitor(int api, ClassVisitor cv) {
        super(api,cv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String sig, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, sig, exceptions);
        if(mv!=null&& !"<init>".equals(name)&&!"main".equals(name)){
            boolean isAbstract = (access&Opcodes.ACC_ABSTRACT)==Opcodes.ACC_ABSTRACT;
            boolean isNative = (access&Opcodes.ACC_NATIVE)== Opcodes.ACC_NATIVE;
            if(!isAbstract&&!isNative){
                mv = new MethodParamAdapter(api, mv,access,name,desc);
            }
        }
        return mv;
    }

    private static class MethodParamAdapter extends MethodVisitor{

        private final int access;
        private final String name;
        private final String desc;

        public MethodParamAdapter(int api, MethodVisitor methodVisitor,int access, String name, String desc) {
            super(api, methodVisitor);
            this.access = access;
            this.name = name;
            this.desc = desc;
        }

        @Override
        public void visitCode() {
            boolean isStatic = ((access&ACC_STATIC))!=0;
            int slotIndex = isStatic?0:1;

            printMessage("Method Enter:"+name+desc);

            Type methodType = Type.getMethodType(desc);
            Type[] argTypes = methodType.getArgumentTypes();
            for(Type t:argTypes){
                int sort = t.getSort();
                int size = t.getSize();
                String descriptor = t.getDescriptor();
                //ALOAD:String，这个会自动根据type类型拿到对应的opCode
                int opCode = t.getOpcode(ILOAD);
                super.visitVarInsn(opCode,slotIndex);

                if(sort==Type.BOOLEAN){
                    printBoolean();
                }else if(sort==Type.CHAR){
                    printChar();
                }else if(sort==Type.BYTE||sort==Type.SHORT||sort==Type.INT){
                    printInt();
                }else if(sort==Type.FLOAT){
                    printFloat();
                }else if(sort==Type.LONG){
                    printLong();
                }else if(sort==Type.DOUBLE){
                    printDouble();
                }else if(sort==Type.OBJECT&&"Ljava/lang/String;".equals(descriptor)){
                    printString();
                }else if (sort == Type.OBJECT) {
                    printObject();
                }else {
                    printMessage("No Support");
                }
                slotIndex += size;
            }
            super.visitCode();
        }

        @Override
        public void visitInsn(int opcode) {
            // 首先，处理自己的代码逻辑
            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
                printMessage("Method Exit:");
                if (opcode == IRETURN) {
                    super.visitInsn(DUP);
                    printInt();
                }
                else if (opcode == FRETURN) {
                    super.visitInsn(DUP);
                    printFloat();
                }
                else if (opcode == LRETURN) {
                    super.visitInsn(DUP2);
                    printLong();
                }
                else if (opcode == DRETURN) {
                    super.visitInsn(DUP2);
                    printDouble();
                }
                else if (opcode == ARETURN) {
                    super.visitInsn(DUP);
                    printObject();
                }
                else if (opcode == RETURN) {
                    printMessage("    return void");
                }
                else {
                    printMessage("    abnormal return");
                }
            }

            // 其次，调用父类的方法实现
            super.visitInsn(opcode);
        }

        private void printBoolean(){
            super.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
            super.visitInsn(SWAP);
            super.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(Z)V",false);
        }

        private void printChar(){
            super.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
            super.visitInsn(SWAP);
            super.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(C)V",false);
        }


        private void printInt(){
            super.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
            super.visitInsn(SWAP);
            super.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(I)V",false);
        }

        private void printFloat(){
            super.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
            super.visitInsn(SWAP);
            super.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(F)V",false);
        }

        private void printLong(){
            super.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
            super.visitInsn(DUP_X2);
            super.visitInsn(POP);
            super.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(J)V",false);
        }

        private void printDouble(){
            super.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
            super.visitInsn(DUP_X2);
            super.visitInsn(POP);
            super.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(D)V",false);
        }

        private void printString() {
            super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            super.visitInsn(SWAP);
            super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        }

        private void printObject() {
            super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            super.visitInsn(SWAP);
            super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
        }

        private void printMessage(String str) {
            super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            super.visitLdcInsn(str);
            super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        }
    }


}
